print("world generator thread started")
require'love.math'

-- INITIAL SETUP
local air_tile = 0
local water_tile = 1
local land_tile = 2

local iter_a = 5 

local stat_chan = love.thread.getChannel("world_stats")
local stats
if stat_chan:peek() then
	stats = stat_chan:pop()
end

local seed_chan = love.thread.getChannel("world_seed")
local seed
if seed_chan:peek() then
	seed = seed_chan:pop()
end
love.math.setRandomSeed(seed)
print("seed = " .. seed)
-- END INIT

-- LOCAL FUNCTIONS
-- array to hold tiles to send so as to not spam
-- channel
local tile_push_queue = {}
function push_tile(tile)
	table.insert(tile_push_queue, 1, tile)
	--local new_tile = love.thread.getChannel("new_tile")			
	--new_tile:push(tile)
end

-- array to hold previously sent progress so as not
-- to spam and overload the channel
local pushed_progs = {}
-- sends progress which eventually shows up in gui
-- lets the user know whats going on
function push_progress(progress)
	-- if progress has been sent before
	-- return function and do not send
	for i = 1, #pushed_progs do
		if pushed_progs[i] == progress then
			return
		end
	end
	pushed_progs[#pushed_progs+1] = progress
	local progress_c = love.thread.getChannel("wg_progress")
	progress_c:push(progress)
end

-- must be reset every time numbers are reused
-- or else they won't show
function reset_progress_queue()
	pushed_progs = {}
end

function get_percent(lesser, greater)
	local n = lesser/greater
	n = math.floor(n*100)
	return n
end

function get_tile_opts(z)
	if z <= stats.sea then
		return land_tile, water_tile
	else
		return land_tile, air_tile
	end
end

function get_random_tile(x, y, z)
	local r = love.math.random(0,100)
	local a_tile, b_tile = get_tile_opts(z)

	if r <= 45 then
		return a_tile
	else
		return b_tile
	end

end

function get_tile(x, y, z)
	if world_table[z] then
		if world_table[z][y] then
			if world_table[z][y][x] then
				return world_table[z][y][x]
			end
		end
	end
end

function get_vert_tiles(x, y, z)
	return get_tile(x, y, z-1), get_tile(x, y, z+1)
end

function surrounding_walls(x, y, z, distX, distY)

	local distX = distX or 1
	local distY = distY or 1
	local walls = 0
	local a_type, b_type = get_tile_opts(z)

	if distX == 1 and distY == 1 then
		if world_table[z][y][x] then
			if world_table[z][y-1] then
				if world_table[z][y-1][x].type == a_type then walls = walls+1 end
				if world_table[z][y-1][x+1] then
					if world_table[z][y-1][x+1].type == a_type then walls = walls+1 end
				end
				if world_table[z][y-1][x-1] then
					if world_table[z][y-1][x-1].type == a_type then walls = walls+1 end
				end
			end
			if world_table[z][y] then
				if world_table[z][y][x+1] then
					if world_table[z][y][x+1].type == a_type then walls = walls+1 end
				end
				if world_table[z][y][x-1] then
					if world_table[z][y][x-1].type == a_type then walls = walls+1 end
				end
			end
			if world_table[z][y+1] then				
				if world_table[z][y+1][x].type == a_type then walls = walls+1 end
				if world_table[z][y+1][x+1] then
					if world_table[z][y+1][x+1].type == a_type then walls = walls+1 end
				end
				if world_table[z][y+1][x-1] then
					if world_table[z][y+1][x-1].type == a_type then walls = walls+1 end
				end
			end
			
		end
	else
		for y1 = 1, distY do
			for x1 = 1, distX do
				local tile, tile2
				if world_table[z][y+y1-1] then
					if world_table[z][y+y1-1][x+x1-1] then
						tile = world_table[z][y+y1-1][x+x1-1].type
					end
				end
				if world_table[z][y-y1] then
					if world_table[z][y-y1][x-x1] then
						tile2 = world_table[z][y-y1][x-x1].type
					end
				end

				if tile == a_type then walls = walls+1 end
				if tile2 == a_type then walls = wall+1 end
			end
		end
	end

	return walls

end

function surrounding_elevation(x, y, z, distX, distY, distZ)
	local distX = distX or 1
	local distY = distY or 1
	local distZ = distZ or 1
	local a_type, b_type = get_tile_opts(z)

	local walls_down = {}
	local walls_up = {}

	local tile = get_tile(x, y, z)

	if distZ == 1 then
		local tile_up, tile_down = get_vert_tiles(x, y, z)
		if z ~= 1 then
			walls_down[1] = surrounding_walls(x, y, z-1, distX, distY)
		end
		if z ~= 32 then
			walls_up[1] = surrounding_walls(x, y, z+1, distX, distY)
		end

		if tile_down == a_type then
			if walls_down[1] >= 4 then
				tile.type = a_type
			end
		elseif tile_up == b_type then
			if walls_up[1] >= 5 then
				tile.type = a_type
			end
		end

	end
end

function refine_tile(x, y, z, i)
	local tile = world_table[z][y][x]
	local a_tile, b_tile = get_tile_opts(z)

	local walls = surrounding_walls(x, y, z, 1, 1)

	if tile.type == a_tile and walls >= 4 then
		tile.type = a_tile
	elseif tile.type == b_tile and walls >= 5 then
		tile.type = a_tile
	--elseif tile.type == b_tile and walls == 0 then
	--	tile.type = a_tile
	else
		tile.type = b_tile
	end

	surrounding_elevation(x, y, z)

end

-- END LOCAL FUNCTIONS


-- MAIN EXECUTION
-- WORLD GENERATION

world_table = {}
push_progress('Beginning generation')

for z = 1, stats.ele do	
	world_table[z] = {}
	for y = 1, stats.lat do
		world_table[z][y] = {}
		for x = 1, stats.long do			
			world_table[z][y][x] = {}
			local tile = world_table[z][y][x]
			tile.y = y
			tile.x = x
			tile.z = z
			tile.type = get_random_tile(x, y, z)
			local pz = get_percent(z,stats.ele)			
			push_progress("Initial Generation " .. pz .. "% complete")			
		end
	end
end

for i = 1, iter_a do
	for z = 1, stats.ele do
		for y = 1, stats.lat do
			for x = 1, stats.long do
				refine_tile(x, y, z, i)

				local pz = get_percent(i,iter_a)
				push_progress("Refining " .. pz .. "% complete")
			end
		end
	end
end

-- END WORLD GENERATION

-- QUEUE TILES TO SEND TO MAIN THREAD
-- a queue  with delay must be created else
-- channels will overflow and send inaccurate
-- data to the main thread

reset_progress_queue()
push_progress('Generation Complete, Queueing Tiles')
-- push all tiles to queue
for z = 1, stats.ele do
	for y = 1, stats.lat do
		for x = 1, stats.long do
			local pz = z/stats.ele
			pz = math.floor(pz*100)
			--print("..")
			push_progress("Queue " .. pz .. "% complete")
			push_tile(world_table[z][y][x])
		end
	end
end

-- loop to send tiles with delay
local dt = 0 --timer count
local delay = 100000 -- timer max, does not work at 10000, but possibly less than 100000
local total_size_queue = #tile_push_queue -- used for progress information
print("tiles .. " .. #tile_push_queue) -- print how many total tiles
reset_progress_queue()
push_progress('Tiles Queued, Sending to World')
while #tile_push_queue > 0 do
	dt = dt+1
	if dt > delay then
		dt = 0
		local tile = table.remove(tile_push_queue)				
		local new_tile = love.thread.getChannel("new_tile")			
		new_tile:push(tile)
		local pz = #tile_push_queue/total_size_queue -- progress
		pz = math.floor(pz*100) -- turn into percent
		--print(pz .. "%")
		push_progress("Sending Tiles to World ".. pz .. "% remaining")
	end
end

local gfc = love.thread.getChannel("world_gen_finished")
gfc:push(true)

-- END
